
local meta = FindMetaTable( "Player" )
if (!meta) then return end 

function meta:IsFemale()
	local playermodel = self:GetInfo( "cl_playermodel" )
	local modelname = player_manager.TranslatePlayerModel( playermodel )
	local has = table.HasValue(GirlyModels,modelname)
return has end

function meta:GetWeight()
return self:GetNWInt("weight",0) end

function meta:AddWeight(num)
	self:SetNWInt("weight",self:GetWeight() + num)
end

function meta:RemoveWeight(num)
	self:SetNWInt("weight",self:GetWeight() - num)
end

function meta:SetWeight(num)
	self:SetNWInt("weight",num)
end

function meta:SetInvSlots(num)
	self:SetNWInt("invslots",num)
end

function meta:GetInvSlots()
return self:GetNWInt("invslots",0) end

function meta:EmptyInventory()
	self:SetWeight(0)
	self.Inventory = {}
	umsg.Start("InvSynch", self)
	umsg.End() 
end

function meta:GetInventory() //all of a players weapons + items
	local weapons = self:GetWeapons()
	local wmodels = {}
	for k,v in pairs(weapons) do
		if v:GetUsedAmmoType() != "none" then
			if not table.HasValue(wmodels,v:GetWorldModel()) then 
				table.insert(wmodels,v:GetWorldModel())
			end
		end
	end
	local tbl = table.Copy(self.Inventory)
	table.Add(tbl,wmodels)
return tbl end

function meta:GetItemInventory() //all of a players items
return self.Inventory end

function meta:GetDeathInventory() //all of a players items + ammo - obsolete?
	local ammos = {"smg1","pistol","buckshot","ar2"}
	local models = {"models/items/boxsrounds.mdl","models/items/357ammobox.mdl","models/items/boxbuckshot.mdl","models/items/boxmrounds.mdl"}
	local tbl = table.Copy(self.Inventory)
	for k,v in pairs(ammos) do
		if self:GetAmmunition(v) > 40 then
			table.insert(tbl,models[k])
		end
	end
return tbl end

function meta:HasInvItem(model)
	for k,v in pairs(self:GetItemInventory()) do
		if v == model then
			return true
		end
	end
	return false
end

function meta:CanAddToInventory(model) 
	local tbl = Inventory.FindTable(model)
	if tbl == nil then return nil end
	local total = tbl.Weight + self:GetWeight()
	if total > self:GetInvSlots() then
		Inventory.Notice(self, "You can't carry that item", Inventory.Red)
		self:ClientSound(Inventory.Deny)
	end
return total <= self:GetInvSlots() end

function meta:AddToInventory(item,modeloverride)
	if modeloverride then
		item = ents.Create("prop_physics_respawnable")
		item:SetModel(modeloverride)
		item:SetPos(self:GetPos() + Vector(0,0,20))
		item:Spawn()
		item:SetColor(0,0,0,0)
		item:SetHealth(150)
	end
	local tbl = Inventory.FindTable(item:GetModel())
	if self.Inventory then
		table.insert(self.Inventory,tbl.Model)
		self:AddWeight(tbl.Weight)
	else
		self.Inventory = {tbl.Model}
		self:SetWeight(tbl.Weight)
	end
	if item:IsValid() then
		Inventory.RemoveEnt(item)
	end
	self:EmitSound(Inventory.ItemPickup,100,math.random(90,100))
	Inventory.Notice(self, "Picked up item: "..tbl.Name, Inventory.Green)
	self:SynchInventory()
	return self:GetInventory()
end

function meta:RemoveFromInventory(item)
	local slot = nil
	local weight
	for k,v in pairs(self.Inventory) do
		if v == item then
			slot = k
			weight = Inventory.FindTable(v).Weight or 1
		end
	end
	if slot then
		self:RemoveWeight(weight)
		table.remove(self.Inventory,slot)
	end
	self:SynchInventory()
return self:GetInventory() end

function meta:SynchInventory()
	umsg.Start("InvSynch", self)
	umsg.End() 
	for k,v in pairs(self:GetInventory()) do
		umsg.Start("InvInsert", self)
		umsg.String(v)
		umsg.End() 
	end
end

function meta:LookupTable(tbl,name)
	for k,v in pairs(tbl) do
		if v.Name == name then
			return tbl[k]
		end
	end
end

function meta:ResetAutoBuy()
	self.AutoBuy = nil
end

function meta:ClientMsg(txt)
	umsg.Start("CMsg", self)
	umsg.String(txt)
	umsg.End() 
end

function meta:ClientSound(sound)
	umsg.Start("PlaySound", self)
	umsg.String(sound)
	umsg.End() 
end

function meta:VoiceSound(sound,vol,delay)
	if not self:Alive() then return end
	vol = vol or 100
	self.Pitch = self.Pitch or {95,105}
	local rnum = math.random(self.Pitch[1],self.Pitch[2])
	if self.SoundDelay then
		if self.SoundDelay < CurTime() then
			if delay != nil then
				timer.Simple(delay, function(self,sound,vol,rnum) 
				if self.SoundDelay or 0 < CurTime() then
					if self:IsValid() then
						self:EmitSound(sound,vol,rnum) end
						self.SoundDelay = CurTime() + 2
					end
				end, self, sound, vol, rnum)
			else
				self.SoundDelay = CurTime() + 2
				self:EmitSound(sound,vol,rnum)
			end
		end
	else
		self.SoundDelay = CurTime() + 2
		if delay != nil then
			timer.Simple(delay, function(self,sound,vol,rnum) 
			if self.SoundDelay < CurTime() then
				if self:IsValid() then
					self:EmitSound(sound,vol,rnum) end
					self.SoundDelay = CurTime() + 2
				end
			end, self, sound, vol, rnum)
		else
			self.SoundDelay = CurTime() + 2
			self:EmitSound(sound,vol,rnum)
		end
	end
end

function meta:SpawnGasClouds(pos)
	local gas = ents.Create( "sent_poisongas" )
	gas:SetPos( pos )
	gas:SetOwner( self )
	gas:Spawn()
	gas:Activate()
end

function meta:DoStamina()
	if self:KeyDown(IN_SPEED) and self:GetVelocity() != Vector(0,0,0) then
		if not self.StaminaSap then
			self.StaminaSap = CurTime() + 1
		elseif self.StaminaSap < CurTime() then
			self:TakeStamina( GameInfo.StaminaLoss )
			self.StaminaSap = nil
		end
		return
	end
	if not self.StaminaRegen then
		self.StaminaRegen = CurTime() + 3
	elseif self.StaminaRegen < CurTime() then
		self:AddStamina(GameInfo.StaminaRegen)
		self.StaminaRegen = nil
	end
end

function meta:DoBlind(attacker)
	self.BlindTime = CurTime() + math.random(10,15)
	self.Blinder = attacker
	self:SetNetworkedBool("blinded",true)
	self:ClientSound(FVOX.Blinded)
end

function meta:StopBlindness()
	self.BlindTime = nil
	self:SetNetworkedBool("blinded",false)
end

function meta:DoBlindness()
	if not self.BlindTime then return end
	
	if self.BlindTime < CurTime() then
		self:StopBlindness()
		self:VoiceSound(ChooseRandom(self.Pain),100)
		
		local trace = util.GetPlayerTrace(self,Vector(0,0,-1))
		local tr = util.TraceLine(trace) 
		util.Decal("Blood", tr.HitPos + tr.HitNormal, tr.HitPos - tr.HitNormal) 
		
		if self.Blinder then
			if self.Blinder:IsValid() then
				self:TakeDamage( math.random(5,15), self.Blinder, self.Blinder )
				self:TakeStamina(50)
			end
		else
			self:TakeHealth(math.random(5,15))
			self:TakeStamina(50)
		end
		self:ViewPunch(Angle(math.Rand(-0.3,0.2) * math.random(5,10), math.Rand(-0.15,0.15) * math.random(5,10), 0))
	end
end

function meta:DoBleed(attacker)
	if not self:Alive() then return end
	self.BleedInterval = CurTime() + math.random(5,10) //give it a delay
	self.Cutter = attacker
	self:SetNetworkedBool("bleeding",true)
	self:VoiceSound(ChooseRandom(self.Pain),100)
	self:ClientSound(FVOX.Bleeding)
end

function meta:StopBleeding()
	if (self.Cutter or self.BleedInterval) and self:Alive() and self:Health() > 5 then
		self:ClientSound(FVOX.Aid)
	end
	self.BleedInterval = nil
	self.Cutter = nil
	self:SetNetworkedBool("bleeding",false)
end

function meta:DoBleeding()
	if not self.BleedInterval then return end
	
	if self.BleedInterval < CurTime() then
		self.BleedInterval = CurTime() + math.Rand(5,10)
		self:VoiceSound(ChooseRandom(self.Pain),100,.5)
		
		local trace = util.GetPlayerTrace(self,Vector(0,0,-1))
		local tr = util.TraceLine(trace) 
		util.Decal("Blood", tr.HitPos + tr.HitNormal, tr.HitPos - tr.HitNormal) 
		
		local damage = math.random(3,6)
		if self:GetStamina() < 1 then
			damage = math.random(5,8)
		end
		
		if self.Cutter then
			if self.Cutter:IsValid() then
				self:TakeDamage( damage, self.Cutter, self.Cutter )
			end
		else
			self:TakeHealth(damage)
		end
		self:TakeStamina(30)
		self:ViewPunch(Angle(math.Rand(-0.3,0.2) * math.random(5,10), math.Rand(-0.15,0.15) * math.random(5,10), 0))
	end
end

function meta:SetImmune(b)
	self.Immunity = b
end

function meta:Immune()
return self.Immunity end

function meta:StopInfection()
	if (self.Infector or self.InfectInterval) and self:Alive() and self:Health() > 5 then
		self:ClientSound(FVOX.Antidote)
	end
	self.InfectInterval = nil
	self.Infector = nil
	self:SetNetworkedBool("infected",false)
end

function meta:DoInfection(att)
	if not self:Alive() then return end
	if not self.Infector or not self.InfectInterval then
		self:AddAwardPoints(AWARD_BAD_INFECT,1)
	end
	self.InfectInterval = CurTime() + 5 //give it a delay
	self.Infector = att
	self:SetNetworkedBool("infected",true)
	self:VoiceSound(ChooseRandom(self.Cough),100)
	self:ClientSound(FVOX.Infected)
end

function meta:DoInfected()
	if not self.InfectInterval then return end
	
	if self.InfectInterval < CurTime() then
		self.InfectInterval = CurTime() + math.Rand(4,8)
		self:VoiceSound(ChooseRandom(self.Cough),100,.5)
		self:TakeStamina(15)
		
		local damage = math.random(3,5)
		if self:GetStamina() < 15 then
			damage = 5
		end
		
		if self.Infector then
			if self.Infector:IsValid() then
				self:TakeDamage( damage, self.Infector, self.Infector )
			end
		else
			self:TakeHealth(damage)
		end
		self:ViewPunch(Angle(math.Rand(-0.3,0.2) * math.random(5,15), math.Rand(-0.15,0.15) * math.random(5,15), 0))
	end
end

function meta:DoPoison(attacker)
	if not self:Alive() then return end
	if self:HasInvItem("models/items/combine_rifle_cartridge01.mdl") then return end
	self.PoisonInterval = CurTime() + 5 //give it a delay
	self.Poisoner = attacker
	self:SetNetworkedBool("poisoned",true)
	self:VoiceSound(ChooseRandom(self.Cough),100)
	self:AddAwardPoints(AWARD_BAD_INFECT,1)
	self:ClientSound(FVOX.Poisoned)
end

function meta:StopPoison()
	if (self.Poisoner or self.PoisonInterval) and self:Alive() and self:Health() > 5 then
		self:ClientSound(FVOX.Antidote)
	end
	self.PoisonInterval = nil
	self.Poisoner = nil
	self:SetNetworkedBool("poisoned",false)
end

function meta:DoPoisoned()
	if not self.PoisonInterval then return end
	
	if self.PoisonInterval < CurTime() then
		self.PoisonInterval = CurTime() + math.Rand(4,8)
		self:VoiceSound(ChooseRandom(self.Cough),100,.5)
		self:TakeStamina(10)

		local damage = math.random(5,10)
		if self:GetStamina() < 15 then
			damage = math.random(8,10)
		end
		
		if self.Poisoner then
			if self.Poisoner:IsValid() then
				self:TakeDamage( damage, self.Poisoner, self.Poisoner )
			end
		else
			self:TakeHealth(damage)
		end
		self:ViewPunch(Angle(math.Rand(-0.3,0.2) * math.random(10,15), math.Rand(-0.15,0.15) * math.random(10,15), 0))
	end
end

// Player should be on fire throughout x seconds
function meta:SetFireTime( add )
	self.FireTimer = CurTime() + add 
end

// Returns the amount of seconds left being on fire
function meta:GetFireTime( add )
	return (self.FireTimer or 0) - CurTime()
end

function meta:DoExtinguish()
	self:SetFireTime( -1 )
	self.FireAttacker = nil
	self:SetNetworkedBool("onfire",false)
	if self.BurnSound then
		self.BurnSound:Stop()
	end
end

function meta:DoIgnite( attacker )
	if not self.FireAttacker then
		self:EmitSound(Sound("ambient/fire/mtov_flame2.wav"),100,math.random(90,110))
		local burn = Sound("General.BurningFlesh")
		self.BurnSound = CreateSound(self, burn)
		self.BurnSound:Play()
		if self:Team() == TEAM_HUMANS then
			self:ClientSound(FVOX.Burn)
		end
	end
	if attacker == nil or attacker:Team() == self:Team() or attacker:Team() == TEAM_UNDEAD then
		attacker = self
	end
	if attacker == self then
		self:SetFireTime( math.Rand(1.5,2.5) )
	else
		self:SetFireTime( math.random(10,15) )
	end
	self.FireAttacker = attacker
	if self.FireImmune then
		self:SetFireTime( math.Rand(0.5,1) )
	end
end

function meta:DoOnFire()

	if self:GetFireTime() <= 0 or not self:Alive() then
		if self.OnFire then
			self.OnFire = false
			self:SetNetworkedBool("onfire",false)
			self.BurnSound:Stop()
		end
	return end
	
	if self:GetFireTime() > 0 and not self.OnFire then
		self.OnFire = true
		self:SetNetworkedBool("onfire",true)
		
		local effectdata = EffectData()
		effectdata:SetEntity(self)
		util.Effect("player_immolate", effectdata)
	end

	if (self.LastFire or 0) < CurTime() and self.OnFire then
	
		self.LastFire = CurTime() + 0.1 
		self.Interval = self.Interval or 1
		local rand = math.random(1,20)
		if not self.bInterval then
			self.bInterval = CurTime() + math.Rand(.4,.8)
			if math.random(1,4) != 1 then
				if self:Team() == TEAM_HUMANS then
					self:VoiceSound(ChooseRandom(self.Burn),100,.5)
				else
					self:VoiceSound(ChooseRandom(self.Pain),100,.5)
				end
			else
				self:EmitSound(ChooseRandom(GameSounds.Burn))
			end
		elseif self.bInterval < CurTime() then
			self.bInterval = nil
			self:ViewPunch(Angle(math.Rand(-0.3,0.3) * math.random(50,70), math.Rand(-0.3,0.3) * math.random(50,70), 0))
		end
		if self.FireAttacker and self.FireAttacker != self and self:Team() == TEAM_HUMANS then
			if self:Health() < 10 then
				self.Death = GameSounds.Burn
				self.DeathType = "burn"
			end
			self:TakeDamage( 1, self.FireAttacker, self.FireAttacker )
			self:AddAttackerDamage(self.FireAttacker,1,false)
		else
			self:TakeHealth(1)
			if self:Health() < 10 then
				self.Death = GameSounds.Burn
				self.DeathType = "burn"
			end
		end
	end
end

function meta:DoDrown()
	if self:WaterLevel() > 2 then
		if self.FireAttacker then
			self:DoExtinguish()
		end
		self.DrownTimer = self.DrownTimer or CurTime() + math.random(8,10)
		if self.DrownTimer < CurTime() then
			self.DrownHurt = self.DrownHurt or CurTime() + 1
			if self.DrownHurt < CurTime() then
				self.DrownHurt = nil
				self:TakeHealth(10)
				self:EmitSound(ChooseRandom(GameSounds.Drown),100,math.random(90,110))
			end
		end
	else
		self.DrownTimer = nil
	end
end

function meta:AddAttackerDamage(attacker,dmg,headshot)
	if not attacker:IsPlayer() then return end
	local cancalc = true
	local wep = attacker:GetActiveWeapon()
	if wep and wep != NULL then
		if wep:GetWorldModel() == "models/weapons/w_annabelle.mdl" or wep:GetWorldModel() == "models/weapons/w_shotgun.mdl" then
			cancalc = false
		end
	end
	if headshot and cancalc then
		if attacker.HSCombo then
			attacker.HSCombo = attacker.HSCombo + 1
			if attacker.HSCombo == 5 then
				Inventory.Notice(attacker, "5 headshot combo", Inventory.Yellow)
				attacker:AddPoints(1)
			elseif attacker.HSCombo == 10 then
				Inventory.Notice(attacker, "10 headshot combo", Inventory.Yellow)
				attacker:AddPoints(3)
			elseif attacker.HSCombo == 15 then
				Inventory.Notice(attacker, "15 headshot combo", Inventory.Yellow)
				attacker:AddPoints(6)
			elseif attacker.HSCombo == 20 then
				Inventory.Notice(attacker, "20 headshot combo", Inventory.Yellow)
				attacker:AddPoints(10)
			end
		else
			attacker.HSCombo = 1
		end
	else
		attacker.HSCombo = 0
	end
	if not self.Attackers then
		self.Attackers = {}
		self.Attackers[1] = {Player=attacker, Damage=math.Round(dmg), Headshot=headshot}
	else
		if not self:FindAttackerDamage(attacker) then
			self.Attackers[table.Count(self.Attackers)+1] = {Player=attacker, Damage=math.Round(dmg), Headshot=headshot}
		else
			for k,v in pairs(self.Attackers) do
				if v.Player == attacker then
					v.Damage = math.Round(v.Damage or 0)
					v.Damage = math.Round(v.Damage + dmg)
					v.Headshot = headshot
				end
			end
		end
	end
end

function meta:GetAttackerTable()
return self.Attackers or {} end

function meta:FindAttackerDamage(attacker)
	if not self.Attackers then return nil end
	for k,v in pairs(self.Attackers) do
		if v.Player == attacker then
			return v.Damage, v.Headshot
		end
	end
	return nil
end

function meta:SetAwardPoints(name,num)
	self:SetNetworkedInt(name,num)
end

function meta:AddAwardPoints(name,num)
	self:SetNetworkedInt(name,(self:GetNetworkedInt(name,0)) + num)
end

function meta:GetAwardPoints(name)
return self:GetNetworkedInt(name,0) end

function meta:GetAmmunition( name )
return self:GetNWInt( "ammo_" .. name, 0 ) end

function meta:SetAmmunition( name, num )
	self:SetNWInt( "ammo_" .. name, num )
	self:SetAmmo( num, name )
end

function meta:AddAmmunition( name, num )
	self:SetAmmunition( name, self:GetAmmunition( name ) + num )
	self:SetAmmo( self:GetAmmunition( name ) + num, name )
end

function meta:TakeAmmunition( name, num )
	self:SetNWInt( "ammo_" .. name, math.Clamp(self:GetAmmunition( name ) - num,0,500))
	self:RemoveAmmo( num, name )
end

function meta:AddHealth(value)
	if self:Health() == self.MaxHealth or not self:Alive() then return end
	self:SetHealth(math.Clamp(self:Health() + value, 0, self.MaxHealth))
	if value > 10 then
		umsg.Start("HealthKit", self)
		umsg.End() 
		if self:Team() == TEAM_HUMANS then
			self:ClientSound(FVOX.Vial)
		end
	else
		umsg.Start("HealthTiny", self)
		umsg.End() 
	end
end

function meta:TakeHealth(value)
	if self:Health() - value <= 0 then
		self:Kill()
	else
		self:SetHealth(math.Clamp(self:Health() - value, 0, self.MaxHealth))
	end
end

function meta:GetStamina()
return self:GetNWInt( "stamina",100 ) end

function meta:SetStamina( num )
	self:SetNWInt( "stamina", num )
end

function meta:AddStamina( num )
	self:SetStamina(math.Clamp(self:GetStamina() + num, 0, 100))
end

function meta:TakeStamina( num )
	self:SetNWInt("stamina", math.Clamp(self:GetStamina() - num, 0, 100)) 
end

function meta:HasPoints(price)
return self:GetPoints() >= price end

function meta:GetPoints()
return self:GetNWInt( "points",0 ) end

function meta:SetPoints( num )
	self:SetNWInt( "points", num )
end

function meta:TakePoints( num )
	self:SetNWInt( "points", self:GetPoints() - num )
end

function meta:AddPoints( num )
	if self:Team() == TEAM_UNDEAD then return end
	self:SetPoints(math.Clamp(self:GetPoints() + num, 0, 200))
	if num == 1 then
		Inventory.Notice(self, "+1 "..GameInfo.PointName, Inventory.Yellow)
	else
		Inventory.Notice(self, "+"..num.." "..GameInfo.PointName.."s", Inventory.Yellow)
	end
	if self:GetPoints() >= 10 and not self.PointHint then
		self.PointHint = true
		Inventory.Notice(self, "Press F3 to purchase items", Inventory.White)
	end
end

function meta:AddPowerup(pnum)
	self.Powerups = self.Powerups or {}
	local num = table.Count(self.Powerups)
	if num >= 6 then return end
	local pu = ChooseRandom(Powerups.List)
	if pnum then
		pu = Powerups.List[pnum]
	end
	while table.HasValue(self.Powerups,pu) do
		pu = ChooseRandom(Powerups.List)
	end
	table.insert(self.Powerups,pu)
	Inventory.Notice(self, "Gained upgrade: "..pu, Inventory.Yellow)
	self:ClientSound(Inventory.Alert)
end

function meta:SpawnPlutonium(amt,ply)
	for i=1,amt do
		local plut = ents.Create("sent_plutonium")
		plut:SetPos(self:GetPos() + Vector(0,0,math.random(30,60)))
		plut:SetAngles(Angle(math.random(-100,100),math.random(-100,100),math.random(-100,100)))
		plut:Spawn()
		if not ply:Alive() or ply:Team() != TEAM_HUMANS then
			if team.NumPlayers(TEAM_HUMANS) > 1 then
				plut:SetTarget(ChooseRandom(team.GetPlayers(TEAM_HUMANS)))
			else
				plut:SetTarget(ply)
			end
		else
			plut:SetTarget(ply)
		end
	end
end